/*
*Copyright 2021-2023 NERCIS
*
*Licensed under the Apache License, Version 2.0 (the "License");
*you may not use this file except in compliance with the License.
*You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*Unless required by applicable law or agreed to in writing, software
*distributed under the License is distributed on an "AS IS" BASIS,
*WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*See the License for the specific language governing permissions and
*limitations under the License.
*/

package cn.ac.nercis.pes.community.controller.evaluation.document;

import cn.ac.nercis.pes.common.constant.evaluation.InfoLevel;
import cn.ac.nercis.pes.community.component.dialog.MessageDialog;
import cn.ac.nercis.pes.community.component.dialog.MessageType;
import cn.ac.nercis.pes.community.component.dialog.PageDialog;
import cn.ac.nercis.pes.community.component.dialog.SpinnerDialog;
import cn.ac.nercis.pes.community.constant.AppConstants;
import cn.ac.nercis.pes.community.controller.base.InitEvalDataLoad;
import cn.ac.nercis.pes.community.manager.RealTimeSaveManager;
import cn.ac.nercis.pes.community.service.SysConfigService;
import cn.ac.nercis.pes.community.utils.AppStore;
import cn.ac.nercis.pes.community.utils.AppUtils;
import cn.ac.nercis.pes.community.utils.LogUtils;
import cn.ac.nercis.pes.community.utils.SpringUtils;
import cn.ac.nercis.pes.community.views.evaluation.document.GenerateDocView;
import cn.ac.nercis.pes.community.views.evaluation.document.IntegrityVerificationView;
import cn.ac.nercis.pes.model.dto.evaluation.analyze.IntegrityVerificationDTO;
import cn.ac.nercis.pes.model.dto.report.EvaluationReportDTO;
import cn.ac.nercis.pes.service.document.IntegrityVerificationService;
import cn.ac.nercis.pes.service.document.PesReportService;
import cn.ac.nercis.pes.service.document.ReportInfoService;
import javafx.application.Platform;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.layout.StackPane;
import javafx.stage.FileChooser;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;

import java.io.File;
import java.net.URL;
import java.util.List;
import java.util.Objects;
import java.util.ResourceBundle;
import java.util.concurrent.CompletableFuture;

/**
 * 密评方案生成控制器
 * @author zpy
 * @version 1.0.0
 */
@Slf4j
public class ReportDocController implements Initializable, InitEvalDataLoad {


    @FXML
    private TextField codeTxt;

    @FXML
    private Label levelLbl;
    @FXML
    private Label evalCorrectionLbl;
    @FXML
    private Label scoreLbl;
    @FXML
    private TextArea descTxt;

    private final ReportInfoService reportInfoService;
    private final SysConfigService sysConfigService;
    private final PesReportService pesReportService;
    private final IntegrityVerificationService verificationService;
    private final RealTimeSaveManager realTimeSaveManager = new RealTimeSaveManager(this::realTimeSave);

    private EvaluationReportDTO currentReportInfo;
    private PageDialog pageDialog;
    private StackPane page;
    private String currentSystemId;

    public ReportDocController() {
        this.reportInfoService = SpringUtils.getBean(ReportInfoService.class);
        this.pesReportService = SpringUtils.getBean(PesReportService.class);
        this.sysConfigService = SpringUtils.getBean(SysConfigService.class);
        this.verificationService = SpringUtils.getBean(IntegrityVerificationService.class);
    }

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        this.realTimeSaveManager.listenerTextChange(descTxt);
        this.realTimeSaveManager.listenerTextChange(codeTxt);
    }

    @Override
    public void initializeData(String systemId){
        LogUtils.info(log, "初始化密评报告生成信息...");
        this.currentSystemId = systemId;
        var view = AppStore.getEvaluationView(this.currentSystemId, GenerateDocView.class);
        if(view.getController() instanceof GenerateDocController controller){
            this.page = controller.getPage();
        }
        loadReportInfo();
    }

    @FXML
    public void onSave() {
        LogUtils.info(log, "执行保存操作...");
        if (!validContent()) {
            return;
        }
        collectionReportInfo();
        LogUtils.info(log, "保存密评报告信息...");
        LogUtils.record(log, "请求数据:{}", currentReportInfo);
        CompletableFuture.supplyAsync(() -> reportInfoService.saveEvaluationReportInfo(currentReportInfo))
                .thenAccept(res -> Platform.runLater(() -> {
                    if (res.isSuccess()) {
                        this.currentReportInfo = res.getData();
                        integrityVerification();
                    } else {
                        MessageDialog.builder()
                                .withType(MessageType.FAIL).withMessage(res.getMsg())
                                .withContainer(Objects.requireNonNull(this.page))
                                .build().show();
                    }
                }))
                .exceptionally((ex) -> {
                    log.error("保存密评报告信息出错!", ex);
                    return null;
                });
    }

    public void realTimeSave(){
        LogUtils.info(log, "执行即时保存操作...");
        collectionReportInfo();
        LogUtils.info(log, "保存密评报告信息...");
        LogUtils.record(log, "请求数据:{}", currentReportInfo);
        CompletableFuture.supplyAsync(() -> reportInfoService.saveEvaluationReportInfo(currentReportInfo))
                .thenAccept(res -> Platform.runLater(() -> {
                    if (res.isSuccess()) {
                        this.initReportInfo(res.getData());
                    } else {
                        log.error("即时保存密评报告信息失败：{}",res.getMsg());
                    }
                }))
                .exceptionally((ex) -> {
                    log.error("保存密评报告信息出错!", ex);
                    return null;
                });
    }

    private boolean validContent() {
        if (StringUtils.isBlank(codeTxt.getText())) {
            AppUtils.showMessageDialog("报告编号不允许为空！", MessageType.WARN, Objects.requireNonNull(this.page));
            codeTxt.requestFocus();
            return false;
        }
        if (StringUtils.isBlank(descTxt.getText())) {
            AppUtils.showMessageDialog("报告分发范围不允许为空！", MessageType.WARN, Objects.requireNonNull(this.page));
            descTxt.requestFocus();
            return false;
        }
        return true;
    }

    private void collectionReportInfo(){
        currentReportInfo.setCode(StringUtils.trim(codeTxt.getText()));
        currentReportInfo.setDistributionRange(StringUtils.trim(descTxt.getText()));
    }

    private void loadReportInfo() {
        LogUtils.info(log, "获取密评报告信息...");
        LogUtils.record(log, "请求数据:{}", this.currentSystemId);
        CompletableFuture.supplyAsync(() -> reportInfoService.getEvaluationReportInfo(this.currentSystemId))
                .thenAccept(res -> Platform.runLater(() -> {
                    if (res.isSuccess()) {
                        initReportInfo(res.getData());
                    } else {
                        MessageDialog.builder()
                                .withType(MessageType.FAIL).withMessage(res.getMsg())
                                .withContainer(this.page)
                                .build().show();
                    }
                }))
                .exceptionally((ex) -> {
                    log.error("获取密评报告信息出错!", ex);
                    return null;
                });
    }


    private void initReportInfo(EvaluationReportDTO evaluationReportDTO){
        realTimeSaveManager.setEnable(false);
        currentReportInfo = evaluationReportDTO;
        codeTxt.setText(currentReportInfo.getCode());
        levelLbl.setText(currentReportInfo.getSecurityLevel());
        evalCorrectionLbl.setText(currentReportInfo.getConclusion());
        scoreLbl.setText(currentReportInfo.getScore());
        descTxt.setText(currentReportInfo.getDistributionRange());
        realTimeSaveManager.setEnable(true);
    }

    private void integrityVerification() {
        //选择保存文件路径
        File storageDir = FileUtils.getFile(this.sysConfigService.getReportDocStoragePath());
        if (!storageDir.exists()) {
            MessageDialog.builder()
                    .withType(MessageType.WARN).withMessage("报告存储路径不存在，请设置默认存储路径！")
                    .withContainer(Objects.requireNonNull(this.page))
                    .build().show();
            return;
        }
        SpinnerDialog spinnerDialog = SpinnerDialog.builder()
                .withProcessTip("正在进行完整性验证...")
                .withContainer(Objects.requireNonNull(this.page))
                .build();
        spinnerDialog.show();
        LogUtils.info(log, "进行完整性验证...");
        LogUtils.record(log, "请求数据:{}", this.currentSystemId);
        CompletableFuture.supplyAsync(() -> verificationService.checkIntegrityByReport(this.currentSystemId))
                .thenAccept(res -> Platform.runLater(() -> {
                    spinnerDialog.close();
                    renderIntegrityVerification(res);
                }))
                .exceptionally((ex) -> {
                    log.error("进行完整性验证出错!", ex);
                    return null;
                });
    }

    private void renderIntegrityVerification(List<IntegrityVerificationDTO> integrityVerifications) {
        long finishCount = integrityVerifications.stream().filter(iv -> iv.getLevel() == InfoLevel.INFO).count();
        var view = AppStore.getEvaluationView(this.currentSystemId, IntegrityVerificationView.class);
        String saveBtnTxt = finishCount == integrityVerifications.size() ? "生成报告" : "继续生成报告";
        boolean notError = integrityVerifications.stream().noneMatch(iv->iv.getLevel()==InfoLevel.ERROR);
        pageDialog = PageDialog.builder()
                .withIcon("dialog-edit-icon")
                .withTitle("完整性验证结果")
                .withContent(Objects.requireNonNull(view).load())
                .withOwner(AppStore.getCurrentSystem(this.currentSystemId).getEvaluationStage())
                .withShowActions(notError)
                .build();
        pageDialog.setYesText(saveBtnTxt);
        pageDialog.setNoText("取消");
        if(view.getController() instanceof IntegrityVerificationController controller){
            controller.initializeData(integrityVerifications);
        }
        pageDialog.show(this::generateReport);
    }

    private void generateReport() {
        pageDialog.close();
        var currentSystem = AppStore.getCurrentSystem(this.currentSystemId);
        String defaultFileName = currentSystem.getSystemName().concat(AppConstants.MP_BG_FILE_NAME).concat(".docx");
        File filePath = FileUtils.getFile(FileUtils.getFile(this.sysConfigService.getReportDocStoragePath()), defaultFileName);
        if (filePath.exists()) {
            FileChooser fileChooser = new FileChooser();
            fileChooser.setTitle("生成的密评报告已存在");
            fileChooser.setInitialDirectory(FileUtils.getFile(this.sysConfigService.getReportDocStoragePath()));
            fileChooser.setInitialFileName(defaultFileName);
            fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("Word 文档", "*.docx"));
            filePath = fileChooser.showSaveDialog(currentSystem.getEvaluationStage());
            if(Objects.isNull(filePath)){
                return;
            }
        }
        generateReport(filePath);
    }

    private void generateReport(File filePath) {
        var spinnerDialog = SpinnerDialog.builder()
                .withProcessTip("正在生成密评报告...")
                .withContainer(Objects.requireNonNull(this.page))
                .build();
        pesReportService.setOnProgress((event, args) -> Platform.runLater(() -> {
            spinnerDialog.getSpinner().setProgress(args.getValue());
            spinnerDialog.setProcessTip(args.getMessage());
        }));
        spinnerDialog.show();
        LogUtils.info(log, "生成密评报告...");
        LogUtils.record(log, "请求数据，systemId：{}", this.currentSystemId);
        CompletableFuture.supplyAsync(() -> pesReportService.generateReport(this.currentSystemId, filePath))
                .thenAccept(res -> Platform.runLater(() -> {
                    spinnerDialog.close();
                    if (res.isSuccess()) {
                        MessageDialog.builder()
                                .withType(MessageType.SUCCESS).withMessage("密评报告生成成功")
                                .withContainer(Objects.requireNonNull(this.page))
                                .build().show();
                    } else {
                        MessageDialog.builder()
                                .withType(MessageType.FAIL).withMessage(res.getMsg())
                                .withContainer(Objects.requireNonNull(this.page))
                                .build().show();
                    }
                }))
                .exceptionally((ex) -> {
                    spinnerDialog.close();
                    log.error("生成密评报告出错!", ex);
                    return null;
                });
    }

}